package com.googlecode.placesapiclient.client.util;

import com.googlecode.placesapiclient.client.argument.annotation.ArgumentMapping;
import com.googlecode.placesapiclient.client.argument.annotation.ArgumentMappings;
import com.googlecode.placesapiclient.client.service.ServiceName;
import com.googlecode.placesapiclient.client.argument.ArgumentMap;
import com.googlecode.placesapiclient.client.connection.ConnectionFactory;
import com.googlecode.placesapiclient.client.connection.proxy.ProxyConfiguration;
import com.googlecode.placesapiclient.client.exception.ErrorCode;
import com.googlecode.placesapiclient.client.exception.ErrorCodeException;
import org.apache.log4j.Logger;
import org.json.JSONException;
import org.json.JSONObject;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.net.URL;
import java.net.URLConnection;

/**
 */
public class Utils {

    private static final Logger logger = Logger.getLogger(Utils.class.getName());

    public static String getUrlConnectionInputStreamToString(URLConnection urlConnection) throws IOException {
        StringBuilder content = new StringBuilder();
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(urlConnection.getInputStream()), 8);
        String line;
        while ((line = bufferedReader.readLine()) != null) {
            content.append(line).append("\n");
        }
        bufferedReader.close();
        return content.toString();
    }

    public static JSONObject getJsonObject(String url, ProxyConfiguration proxyConfiguration) throws ErrorCodeException {
        try {
            String contents = getUrlContents(url, proxyConfiguration);
            logger.info(contents);
            return new JSONObject(contents);
        } catch (JSONException e) {
            logger.error("JSONException", e);
            throw new ErrorCodeException(-13, "JSONException", e);
        }

    }

    public static String getUrlContents(String theUrl, ProxyConfiguration proxyConfiguration) throws ErrorCodeException {
        try {
            URL url = new URL(theUrl);

            URLConnection urlConnection = ConnectionFactory.getURLConnection(url, proxyConfiguration);
            return getUrlConnectionInputStreamToString(urlConnection);
        } catch (IOException e) {
            logger.error("UnexpectedException", e);
            throw new ErrorCodeException(-13, "UnexpectedException", e);
        }
    }


    public static String makeUrl(String baseUrl, ArgumentMap argumentMap, ServiceName serviceName) throws ErrorCodeException {
        try {

            if (baseUrl == null) {
                throw new ErrorCodeException(ErrorCode.MISSING_URL, "baseUrl is required");
            }
            if (argumentMap == null) {
                throw new ErrorCodeException(ErrorCode.MISSING_ARGUMENT_MAP, "baseUrl is required");
            }
            StringBuilder urlString = new StringBuilder(baseUrl);

            Class cl = argumentMap.getClass();

            // Checking all the fields for annotations
            for (Field f : cl.getDeclaredFields()) {

                try {
                    // Processing all the annotations on a single field
                    for (Annotation annotation : f.getAnnotations()) {
                        // Checking for a NullValueValidate annotation
                        if (annotation.annotationType() == ArgumentMappings.class) {
                            ArgumentMappings argumentMappings = (ArgumentMappings) annotation;
                            // Setting the field to be accessible from our class is it is a private member of the class under processing
                            // The setAccessible method will not work if you have Java SecurityManager configured and active.
                            f.setAccessible(true);

                            for (ArgumentMapping argumentMapping : argumentMappings.mappings()) {
                                if (argumentMapping.serviceName().equals(serviceName)) {

                                    switch (argumentMapping.scope()) {
                                        case REQUIRED:
                                            if(f.get(argumentMap) == null) {
                                                throw new ErrorCodeException(ErrorCode.INVALID_PARAMETER, "Missing  required parameter: " + argumentMapping.mapping()
                                                        + ". Please set " + f.getName() + " parameter in ArgumentMap.");
                                            }
                                        case OPTIONAL:
                                            if (f.get(argumentMap) != null) {

                                                if (baseUrl.endsWith("\\?")) {
                                                        urlString.append(argumentMapping.mapping()).append("=").append(String.valueOf(f.get(argumentMap)));
                                                    } else {
                                                        urlString.append("&").append(argumentMapping.mapping()).append("=").append(String.valueOf(f.get(argumentMap)));
                                                    }
                                            }

                                            break;

                                        case NOT_SUPPORTED:
                                            if (f.get(argumentMap) != null) {
                                                logger.warn("Not supported parameter: " + argumentMapping.mapping() + ", value: " + String.valueOf(f.get(argumentMap)) + " for service: " + serviceName.name());
                                            }
                                            break;
                                    }

                                }
                            }


                        }
                    }
                } catch (Throwable e) {
                    //Silence Exception
                    logger.error("UnexpectedException", e);
                }
            }

            logger.info(urlString.toString());
            return urlString.toString();

        } catch (Exception e) {
            logger.error("UnexpectedException", e);
            throw new ErrorCodeException(-13, "UnexpectedException", e);
        }
    }

}
